/*********************************************************************************************************
* 模块名称：W25Qxx.c
* 摘    要：W25Qxx模块
* 当前版本：1.0.0
* 作    者：jadewo
* 完成日期：2025年05月23日
* 内    容：W25Qxx
* 注    意：
**********************************************************************************************************
* 取代版本：
* 作    者：
* 完成日期：
* 修改内容：
* 修改文件：
*********************************************************************************************************/

/*********************************************************************************************************
*                                              包含头文件
*********************************************************************************************************/
#include "W25Qxx.h"
#include "gd32f30x_conf.h"
#include "SPI.h"
/*********************************************************************************************************
*                                              宏定义
*********************************************************************************************************/
#define W25Q64_PAGE_SIZE 256 //一页的大小，256 字节
#define W25Q64_SECTOR_SIZE (4 * 1024) //扇区大小，字节
#define W25Q64_BLOCK_SIZE (64 * W25Q64_SECTOR_SIZE)
#define W25Q64_SIZE (128 * W25Q64_BLOCK_SIZE)
/*********************************************************************************************************
*                                              枚举结构体
*********************************************************************************************************/

/*********************************************************************************************************
*                                              内部变量定义
*********************************************************************************************************/
const unsigned char byDUMMY = 0xff;
/*********************************************************************************************************
*                                              内部函数声明
*********************************************************************************************************/
static void W25Q64WriteEnable(void);
static void W25Q64WaitForWriteEnd(void);
static void W25Q64WriteStatusReg1(unsigned short status);
static void W25Q64PageProgram(unsigned char *buf, unsigned short len, unsigned int addr);
/*********************************************************************************************************
*                                              内部函数实现
*********************************************************************************************************/
static void W25Q64WriteEnable(void)
{
    CLR_SPI_NSS();
    W25Q64ReadWriteData(0x06);
    SET_SPI_NSS();
}

static void W25Q64WaitForWriteEnd(void)
{
    unsigned char status = 0;
    CLR_SPI_NSS();
    W25Q64ReadWriteData(0x05);

    do
    {
        status = W25Q64ReadWriteData(byDUMMY);
    }
    while((status & 0x01) == 1);

    SET_SPI_NSS();
}

static void W25Q64WriteStatusReg1(unsigned short status)
{
    W25Q64WriteEnable();
    CLR_SPI_NSS();
    W25Q64ReadWriteData(0x01);
    W25Q64ReadWriteData(status & 0xFF);
    W25Q64ReadWriteData((status >> 8) & 0xFF);
    SET_SPI_NSS();
    W25Q64WaitForWriteEnd();
}

static void W25Q64PageProgram(unsigned char *buf, unsigned short len, unsigned int addr)
{
    W25Q64WriteEnable();
    CLR_SPI_NSS();
    W25Q64ReadWriteData(0x02);
    W25Q64ReadWriteData((addr & 0xFF0000) >> 16);
    W25Q64ReadWriteData((addr & 0x00FF00) >> 8);
    W25Q64ReadWriteData(addr & 0xFF);

    while (len--)
    {
        W25Q64ReadWriteData(*buf);
        buf++;
    }

    SET_SPI_NSS();
    W25Q64WaitForWriteEnd();
}
/*********************************************************************************************************
*                                              API函数实现
*********************************************************************************************************/
void InitGD25QXX(void)
{
    InitSPI();  
}

void W25Q64Write(unsigned char *buf, unsigned short len, unsigned int addr)
{
    unsigned char pagenum;
    unsigned char addrbyte; //最低八位地址
    addrbyte = addr % W25Q64_PAGE_SIZE;

    if (len > (W25Q64_PAGE_SIZE - addrbyte))//跨页
    {
        W25Q64PageProgram(buf, W25Q64_PAGE_SIZE - addrbyte, addr);//写满本页
        addr += W25Q64_PAGE_SIZE-addrbyte;
        buf += W25Q64_PAGE_SIZE-addrbyte;
        len -= W25Q64_PAGE_SIZE-addrbyte;
        pagenum = len/W25Q64_PAGE_SIZE;

        while (pagenum--)
        {
            W25Q64PageProgram(buf, W25Q64_PAGE_SIZE, addr);
            addr += W25Q64_PAGE_SIZE;
            buf += W25Q64_PAGE_SIZE;
            len -= W25Q64_PAGE_SIZE;
        }

        W25Q64PageProgram(buf, len, addr);
    }
    else
    {
        W25Q64PageProgram(buf, len, addr);
    }
}

unsigned short W25Q64ReadStatus(void)
{
    unsigned short status=0;

    CLR_SPI_NSS();
    W25Q64ReadWriteData(0x05);
    status = W25Q64ReadWriteData(byDUMMY);
    SET_SPI_NSS();
    CLR_SPI_NSS();
    W25Q64ReadWriteData(0x35);
    status |= W25Q64ReadWriteData(byDUMMY) << 8;
    SET_SPI_NSS();

    return status;
}

void W25Q64Read(unsigned char *buf, unsigned int len, unsigned int addr)
{
    unsigned int i;

    CLR_SPI_NSS();
    W25Q64ReadWriteData(0x03);
    W25Q64ReadWriteData((addr & 0xFF0000) >> 16);
    W25Q64ReadWriteData((addr & 0x00FF00) >> 8);
    W25Q64ReadWriteData(addr & 0xFF);

    for (i = 0; i < len; i++)
    {
        buf[i] = W25Q64ReadWriteData(byDUMMY);
    }

    SET_SPI_NSS();
}

unsigned short W25Q64ReadJEDEID(void)
{
  unsigned short DeviceID = 0;
  
  CLR_SPI_NSS();
  W25Q64ReadWriteData(0x9F);
  W25Q64ReadWriteData(byDUMMY);
  DeviceID = W25Q64ReadWriteData(byDUMMY) << 8;
  DeviceID |= W25Q64ReadWriteData(byDUMMY);
  SET_SPI_NSS();
  
  return DeviceID;
}

unsigned short W25Q64ReadDeviceID(void)
{
  unsigned char DeviceID = 0;
  
  CLR_SPI_NSS();
  W25Q64ReadWriteData(0xAB);
  W25Q64ReadWriteData(byDUMMY);
  W25Q64ReadWriteData(byDUMMY);
  W25Q64ReadWriteData(byDUMMY);
  DeviceID = W25Q64ReadWriteData(0X00);
  SET_SPI_NSS();
  
  return DeviceID;
}

void W25Q64X4KErase(unsigned int addr)
{
    W25Q64WriteEnable();
    CLR_SPI_NSS();
    W25Q64ReadWriteData(0x20);
    W25Q64ReadWriteData((addr & 0xFF0000) >> 16);
    W25Q64ReadWriteData((addr & 0x00FF00) >> 8);
    W25Q64ReadWriteData(addr & 0xFF);
    SET_SPI_NSS();
    W25Q64WaitForWriteEnd();
}

void  W25Q64X32KErase(unsigned int addr)
{
  W25Q64WriteEnable();
  CLR_SPI_NSS();
  W25Q64ReadWriteData(0x52);
  W25Q64ReadWriteData((addr & 0xFF0000) >> 16);
  W25Q64ReadWriteData((addr & 0x00FF00) >> 8);
  W25Q64ReadWriteData(addr & 0xFF);
  SET_SPI_NSS();
  W25Q64WaitForWriteEnd();
}

void  W25Q64X64KErase(unsigned int addr)
{
  W25Q64WriteEnable();
  CLR_SPI_NSS();
  W25Q64ReadWriteData(0xD8);
  W25Q64ReadWriteData((addr & 0xFF0000) >> 16);
  W25Q64ReadWriteData((addr & 0x00FF00) >> 8);
  W25Q64ReadWriteData(addr & 0xFF);
  SET_SPI_NSS();
  W25Q64WaitForWriteEnd();
}
